﻿#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <map>
#include <stack>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include "element.h"
#include "util/tc_common.h"
#include "util/tc_autoptr.h"

/**
 * Tars文件解析类
 *
 */
class TarsParse
    : public tars::TC_HandleBase {
public:
    /**
     * 构造函数
     */
    TarsParse();

    /**
     * 是否支持tars开头的标识
     * @param bWithTars
     */
    void setTars(bool bWithTars);

    /**
     * 头文件路径
     * @param sHeader
     */
    void setHeader(const std::string& sHeader);

    /**
     * 是否支持优先使用当前tars文件
     * @param bWithTars
     */
    void setCurrentPriority(bool bFlag);

    /**
     * 获取头文件路径
     */
    std::string getHeader();

    /**
     * 解析某一个文件
     * @param sFileName
     */
    void parse(const std::string& sFileName);

    /**
     * 错误提示
     * @param msg
     */
    void error(const std::string& msg);

    /**
     * 检查关键字
     * @param s
     *
     * @return int
     */
    int  checkKeyword(const std::string& s);

    /**
     * 下一行
     */
    void nextLine();

    /**
     * 目前解析的文件名称
     *
     * @return string
     */
    std::string getCurrFileName();

    /**
     * tab
     *
     * @return string
     */
    std::string getTab();

    /**
     * 增加tab数
     */
    inline void incTab() { _itab++; }

    /**
     * 减少tab数
     */
    inline void delTab() { _itab--; }

    /**
     * 解析文件
     * @param file
     */
    void pushFile(const std::string& file);

    /**
     * 弹出解析文件
     */
    ContextPtr popFile();

    /**
     * 获取所有的上下文
     *
     * @return std::vector<ContextPtr>
     */
    inline std::vector<ContextPtr> getContexts() { return _vcontexts; }

    /**
     * 获取目前的容器
     *
     * @return ContainerPtr
     */
    ContainerPtr currentContainer();

    /**
     * push容器
     * @param c
     */
    void pushContainer(const ContainerPtr& c);

    /**
     * 目前的上下文
     *
     * @return ContextPtr
     */
    ContextPtr currentContextPtr();

    /**
     * 弹出容器
     *
     * @return ContainerPtr
     */
    ContainerPtr popContainer();

    /**
     * 生成Builtin元素
     * @param kind
     *
     * @return BuiltinPtr
     */
    BuiltinPtr createBuiltin(Builtin::Kind kind, bool isUnsigned = false);

    /**
     * 生成Vector元素
     * @param ptr
     *
     * @return VectorPtr
     */
    VectorPtr createVector(const TypePtr& ptr);

    /**
     * 生成Map元素
     * @param pleft
     * @param pright
     *
     * @return MapPtr
     */
    MapPtr createMap(const TypePtr& pleft, const TypePtr& pright);

    /**
     * 添加结构元素
     * @param sPtr
     */
    void addStructPtr(const StructPtr& sPtr);

    /**
     * 查找结构
     * @param id
     *
     * @return StructPtr
     */
    StructPtr findStruct(const std::string& sid);

    /**
     * 添加枚举元素
     * @param ePtr
     */
    void addEnumPtr(const EnumPtr& ePtr);

    /**
     * 查找结构
     * @param id
     *
     * @return EnumPtr
     */
    EnumPtr findEnum(const std::string& sid);

    /**
     * 检查冲突
     * @param id
     */
    void checkConflict(const std::string& sid);

    /**
     * 查找自定义类型
     * @param sid
     *
     * @return TypePtr
     */
    TypePtr findUserType(const std::string& sid);

    /**
     * 查找名字空间
     * @param id
     *
     * @return NamespacePtr
     */
    NamespacePtr findNamespace(const std::string& id);

    /**
     * 目前的名字空间
     *
     * @return NamespacePtr
     */
    NamespacePtr currentNamespace();

    /**
     * 检查tag的合法性
     * @param i
     */
    void checkTag(int i);

    /**
     * 检查szie的合法性
     * @param i
     */
    void checkSize(int i);

    /**
     * 检查array的合法性
     * @param i
     */
    void checkArrayVaid(const TypePtr& tPtr, int size);

    /**
     * 检查pointer的合法性
     * @param i
     */
    void checkPointerVaid(const TypePtr& tPtr);

    /**
     * 检查常量类型和值是否一致
     * @param c
     * @param b
     */
    void checkConstValue(const TypeIdPtr& tPtr, int b);

    /**
     * 获取文件名
     * @param s
     *
     * @return string
     */
    bool getFilePath(const std::string& s, std::string& file);

    inline void setKeyStruct(const StructPtr& key) {
        _key = key;
    }

    inline StructPtr getKeyStruct() {
        return _key;
    }

    /**
     * 打印文件开头注释
     */
    std::string printHeaderRemark();

    /**
     * 判断当前的标识符是否是枚举变量
     */
    bool checkEnum(const std::string& idName);

    /**

    * 设置include的tars文件全部从当前文件搜寻
    */
    inline void setUseCurrentPath(bool bEnable) { _bUseCurrentPath = bEnable; }

    /**
     * 查找tars文件时,设置include路径
     */
    inline void addIncludePath(const std::string& include) {
        auto v = tars::TC_Common::sepstr<std::string>(include, "; ,", false);
        _vIncludePath.insert(_vIncludePath.end(), v.begin(), v.end());
    }

    /**
     *
     * @param fileName
     * @return
     */
    std::string getFileName(const std::string& fileName);

    /**
     * replace ext
     * @param fileName
     * @param ext
     * @return
     */
    std::string replaceFileName(const std::string& fileName, const std::string& ext);

    /**
     * get absolute filename
     * @param baseDir
     * @param fileName
     * @return
     */
    std::string getAbsoluteFileName(const std::string& baseDir, const std::string& fileName);

protected:
    /**
     * 添加名字空间
     * @param nPtr
     */
    void addNamespacePtr(const NamespacePtr& nPtr);

    /**
     * 初始化
     */
    void initScanner();

    /**
     * 清除
     */
    void clear();

protected:
    bool _bWithTars = false;
    std::map<std::string, int> _keywordMap;
    int _itab = 0;
    StructPtr _key;
    std::stack<ContextPtr> _contexts;
    std::stack<ContainerPtr> _contains;
    std::vector<ContextPtr> _vcontexts;
    std::vector<StructPtr> _structs;
    std::vector<EnumPtr> _enums;
    std::vector<NamespacePtr> _namespaces;
    std::string _sHeader;
    bool _bUseCurrentPath = false;
    bool _bUseCurrentPathFirst = false;
    std::vector<std::string> _vIncludePath;
};

extern int yyparse();
extern int yylex();
extern FILE* yyin, *yyout;

typedef tars::TC_AutoPtr<TarsParse> TarsParsePtr;

extern TarsParsePtr g_parse;
